-- Topolog2.ads        -- PASSIVE
--
-- Passive object providing block interconnection info, turnout info and
-- code to track the position of each magnet as it moves along the layout.
--
-- Based on a graph model of the topology.  Sensors, turnouts and various
-- kinds of track section are all nodes.  Blocks are attributes of nodes.
--
-- This package is state-free: all calls pass a position record that
-- represents one magnet.  Since all operations require the direction of
-- travel, ie polarity, this is passed as a field of the record.
--
-- Author: Rob Allen, Swinburne University of Technology
--
-- 31-May-00 (Topolog derived from old layout and simrail.block_feature_pkg)
--  8-May-02 (added Check_Leaving_Turnout, Feature_At, Feature_Kind_At)
-- 12-May-02 (added Converging to Check_Leaving_Turnout)
-- 18-Jun-02 (branch version, generalised Crossing to Zone)
--  9-May-03 (revise & move Sensor_Block_Numbers to spec)
--  1-May-04 (mainstreamed branch version: has Zones + Crossing
--                      needs revised Rail_Typ)
-- 11-Mar-07 (v2.0) to match Rail_Typ(v3.3) (Dio48def, Pc14defs packed arrays)
--       Next_Pos now updates Pos.Next_Expected (previously Next_Sensor)
-- 23-Mar-07 improved comments
-- 28-May-07 (v2.1) add Next_Sensor_Expected() and renamed Next_Sensor()
--                     to Next_Sensor_Along()
-- 26-Mar-08 (v2.2) converted to 2008 layout, renamed Topolog2, reengineered,
--                  extra operations for the new layout
-- 15-Apr-08 (v2.3) replaced Next_Pos functions by Advance_Pos procedures
-- 18-Apr-08 (v2.4) added Initialise_After,
-- 22-Apr-08  released
-- 25-Apr-08 (v2.5) Find_Next_Sensor_{Along|Expected} replace one-parameter
--           Next_Sensor_{Along|Expected}, revise comments
-- 20-May-08 (v2.6) Sensor_Block_Numbers now 2-D for boundary sensors
-- 26-Jun-08 (v2.8) fix Turnout_Data(15)
--  7-May-09 (v2.8.1) more comments for Turnout_Data, Find_Next_Sensor_Expected
--
-- This code is copyright of the author and may be used within Swinburne
-- only.  This header comment must be reproduced.
--
with Raildefs;
use Raildefs;
package Topolog2 is

   -- PROVIDED OPERATIONS
   --
   -- All operations have parameter Pos: Train_Position
   --
   -- MUTATORS
   --   Initialise_After
   --   Advance_Pos
   --   Resume
   --   Turn_Around
   --
   -- VERY IMPORTANT:
   -- Advance_Pos is meant to be called at each side of a sensor, ie when the
   -- On event is called and again when the Off event occurs.
   --
   -- QUERIES to call with pos at front of train just AFTER pos has advanced
   -- onto a sensor:
   --    Check_Entering_Turnout
   --    Check_Entering_Chained_Turnout
   --    Check_Entering_Crossing
   --    Check_Entering_Zone
   --    Is_Leaving_Powered_Track
   --
   -- QUERIES to call with pos at rear of train just BEFORE pos advances
   -- off a sensor:
   --    Check_Leaving_Turnout
   --    Check_Leaving_Chained_Turnout
   --    Check_Leaving_Crossing
   --    Check_Leaving_Zone
   --
   -- OTHER QUERIES:
   --    Feature_At
   --    Feature_Kind_At
   --    Is_Sensor_At
   --    Check_Turnout_Info_Need
   --    Find_Next_Sensor_Along
   --    Find_Next_Sensor_Expected
   --
   -- PROVIDED TYPES
   --
   -- Major:
   --   Train_Position
   --   Feature_Kind
   --   Feature_Descriptor
   --   Turnout_Descriptor
   -- Minor:
   --   Feature_Idx
   --   Substate_Type
   --   Zone_Id
   --   Chain_Type
   --
   -- PROVIDED CONSTANTS
   -- Major:
   --   Turnout_Data
   --   Is_Boundary_Sensor
   --   Sensor_Block_Numbers
   --   values for Crossing_Id: Main_Diagonal, North_Crossover, ...
   --
   -- REQUIRED TYPES
   --   Raildefs.Block_Idx, Raildefs.Crossing_Idx, Raildefs.Polarity_Type,
   --   Raildefs.Sensor_Idx, Raildefs.Turnout_Idx, Raildefs.Turnout_Pos
   --   (plus subtypes and values of these types and Opposite)

   -- General note: this package uses dummy values "No_xxx" and extended
   -- types "xxx_idx" that include the dummy value, eg Sensor_Idx is
   -- Sensor_Id plus the dummy value No_Sensor.  These are defined in Raildefs
   -- (except for Feature_Idx)

   -- This array is easy to use - it duplicates information below in
   -- Turnout_Data.  deprecated
   Straight_Is_Left : constant
   array (Turnout_Id) of Boolean
      := (1..5|7|10|13|15|17|19 => True, others=>False);

   Max_Features : constant := 126;  -- internal use
   type Feature_Idx is range 0 .. Max_Features;  -- internal use
      -- (not private to aid testing)

   type Feature_Kind is
         (Sensor,
          Turnout,
          Plain_Track,
          Crossing_Track, -- (v2.2) renamed
          Boundary_Zone,  -- track either side of a boundary sensor. (v2.2)
          Dead_Track);    -- unpowered track at each end of block 1 and
   --                        at ends of sidings. (v2.2)

   -- Train_Position represents the position (in a topological sense) of
   -- ONE reflector on a train.  (A train will need two instances of this
   -- type, front and rear.  When the train reverses direction the roles
   -- will change.)
   -- The track layout is divided into features (the smallest are sensors,
   -- 10mm approx long but because this is a topological description of the
   -- layout, actual dimensions are not stored).  It is better to
   -- use the various query functions below than code using feature numbers.
   -- Polarity is stored as a field of Train_Position because most decisions
   -- need to know the direction of travel.
   -- Train_Position is normally modified by either version of Advance_Pos below.
   --
   -- Advance_Pos and Turn_Around maintain Polr, Substate and Next_Expected.
   -- However sometimes when you reverse a train you must maintain
   -- Next_Expected yourself by calling Next_Sensor_Expected.

   type Substate_Type is
         (Norm,
          Just_After,
          Just_Before);

   type Train_Position is
      record
         Block_Num     : Block_Id      := 1;
         Polr          : Polarity_Type := Fwd;
         Within        : Feature_Idx   := 112;       -- internal use.
         Substate      : Substate_Type := Norm;      -- only relevant to sensors
         Next_Expected : Sensor_Idx    := No_Sensor; -- (init to dummy value)
         -- (v2.0, 2007 now maintained by this package)
      end record;
   -- Note: The purpose of Next_Expected is to make handling sensor events
   -- easier; it says which sensor will produce the next interrupt
   -- relevant to this reflector assuming the train keeps moving in the
   -- direction determined by Pol.  If the reflector is over a sensor then
   -- that sensor will next produce an Off event. cf function
   -- Next_Sensor_Along which ignores the current sensor.

   -- Crossing_Idx values -- (v2.2)
   -- No_Crossing : Crossing_Idx := 0 defined in raildefs
   Main_Diagonal    : constant Crossing_Id := 1;
   North_Crossover  : constant Crossing_Id := 2;
   South_Crossover  : constant Crossing_Id := 3;
   North_Tram_Cross : constant Crossing_Id := 4;
   South_Tram_Cross : constant Crossing_Id := 5;

   subtype Zone_Id is Sensor_Idx;   -- (v2.2)

   -- Feature_Descriptor is used internally.  It is defined here because
   -- function Feature_At returns this type.
   --
   type Feature_Descriptor(Kind : Feature_Kind := Plain_Track) is
   record
      --Is_An_End : Boolean := False;
      Block_Num    : Block_Idx;
      case Kind is
         when Sensor =>
            Sid     : Sensor_Id;
            Block2 : Block_Idx;     -- /= No_Block if boundary
         when Turnout =>
            Tid    : Turnout_Id;
            Next_Tu : Feature_Idx;  -- internal use
         when Boundary_Zone =>
            Zid    : Zone_Id;  -- the id of the boundary sensor (v2.2)
         when Crossing_Track =>
            Kid    : Crossing_Id;
         when others =>
            null;
      end case;
   end record;

   -- useful static turnout data:
   -- Use this to find out what block is being entered at the other side
   -- of turnout and what polarity, also the relationship between
   -- left-right and straight-turned and the orientation (converging
   -- -diverging of the turnout to the normal (Fwd) polarity direction
   -- of the turnout centre.  Note: topologically a turnout extends out
   -- to the surrounding (guard) sensors.
   type Turnout_Descriptor is
      record
         Block_Num             : Block_Id; -- eg 5 for turnout 2
         Diverging_When_Normal : Boolean;  -- eg True for turnout 12
         Straight_Is_Left      : Boolean;  -- eg True for turnout 13
         Block_St,
         Block_Tu              : Block_Id; -- eg 13,22 for turnout 12
         Swap_Pol_Tu           : Boolean;  -- eg True for turnout 16
      end record;
   --
   --   eg:  turnout 16
   -- == Block_Num=16 ===\====>=====   ==Block_St=17 ==>===
   --                     \
   --                      ==>=====  ===Block_Tu=22 ==<==
   --
   -- (The above diagram shows an example of an inverting join: the
   -- arrows represent polarity and there is a flip in the Tu branch.)
   -- To maintain continuity polarity must be inverted when moving between
   -- blocks 16 and 22, and 19 and 23 within turnouts 17 and 19.
   -- The Advance_Pos procedures automatically adjust the polr attribute
   -- of Pos.  Knowing what turnout is being entered (discovered by
   -- calling Check_Entering_xxx_Turnout) and the desired turnout setting
   -- you can find what block is ahead by grabbing the fields Block_Num,
   -- Block_Tu and Block_St as appropriate.
   --
   Turnout_Data : constant
   array (Turnout_Id) of Turnout_Descriptor :=
      (1 => (11, True, True, 20, 21, False),
      2 => (5, False, True, 21, 2, False),
      3 => (5, False, True, 5, 3, False),
      4 => (5, False, True, 5, 4, False),
      5 => (5, False, True, 5, 20, False),
      6 => (6, True, False, 7, 14, False),
      7 => (7, False, True, 6, 13, False),
      8 => (7, True, False, 8, 9, False),
      9 => (9, True, False, 10, 18, False),
      10 => (10, False, True, 9, 17, False),
      11 => (11, False, False, 10, 24, False),
      12 => (12, True, False, 13, 22, False),
      13 => (13, True, True, 14, 7, False),
      14 => (14, False, False, 13, 6, False),
      15 => (15, False, True, 14, 23, False),  --v2.8 was 6, F..
      16 => (16, True, False, 17, 22, True),
      17 => (17, True, True, 18, 10, False),
      18 => (18, False, False, 17, 9, False),
      19 => (19, False, True, 18, 23, True) );

   -- A boundary sensor (or "true boundary" sensor) is one that is exactly
   -- at the join between blocks.  There are only 4 (2008).
   --
   Is_Boundary_Sensor : constant
   array(Sensor_Id) of Boolean :=
      (21|30|43|62=>True, others=>False);  -- no margin for coasting here
   -- Note (v2.2, 2008) 'Boundary_Zone's surround these and they are identified
   -- by these sensor numbers.  Each such zone has two pieces of track.
   -- Zones 43 and 62 are very wide!

   -- The following array tells what block a sensor belongs to.
   -- It is no longer used in the package body but is the only
   -- source of the information.
   -- The boundary sensors, 21,30,43,62, can be considered as
   -- belonging to two blocks, so the block being entered
   -- depends on polarity,eg
   --   Sensor_Block_Polarity(Fwd,21)=12,
   --   Sensor_Block_Polarity(Rev,21)=19.
   -- The various subprograms treat them specially.
   -- (No_Block is the dummy value for sensors that are not installed.)
   --
   Sensor_Block_Numbers : constant
   array(Polarity_Type,Sensor_Id) of Block_Idx :=
   ( Fwd=> (
      25|27|29|31|33=>1,  -- todo: move 25
      3|4=>2,
      5|6=>3,
      7|8=>4,
      12|14|16|26|28=>5,
      30|32|34=>6,
      36|38=>7,
      60=>8,
      40|42=>9,
      44|46=>10,
      -- block 11 has no sensors!
      21|23|35=>12,  -- todo: move 35
      37=>13,
      39=>14,
      41=>15,
      43|45=>16,
      47=>17,
      49=>18,  -- todo: move 51
      51|15|17|19=>19,
      18|20|22|24=>20,
      1|2|10=>21,
      58|56|50|48=>22,
      53|55|61|63=>23,
      62|64=>24,
      9|11|13|52|54|57|59=>No_Block  -- todo: install 9,13
     ),
     Rev => (
      25|27|29|31|33=>1,  -- todo: move 25
      3|4=>2,
      5|6=>3,
      7|8=>4,
      12|14|16|26|28=>5,
      30=>5, 32|34=>6,
      36|38=>7,
      60=>8,
      40|42=>9,
      44|46=>10,
      -- block 11 has no sensors!
      21=>19, 23|35=>12,  -- todo: move 35
      37=>13,
      39=>14,
      41=>15,
      43=>15, 45=>16,
      47=>17,
      49=>18,  -- todo: move 51
      51|15|17|19=>19,
      18|20|22|24=>20,
      1|2|10=>21,
      58|56|50|48=>22,
      53|55|61|63=>23,
      62=>8, 64=>24,
      9|11|13|52|54|57|59=>No_Block  -- todo: install 9,13
     )
   );
   -- type for use in Check_*_Turnout calls (v2.2)
   --
   -- No - this turnout does not connect to another turnout
   --      directly or via one sensor
   -- Sensored - there is exactly one sensor between this and
   --      the next "chained" turnout so you might need to "go
   --      slow" to give the user an option to change the 2nd
   --      turnout
   -- Immediate - this turnout connects directly to another so
   --      that you must set up both before entering the first
   --      and when you leave the second you are also leaving
   --      the first
   --
   type Chain_Type is
         (No,
          Sensored,
          Immediate);

   Topology_Error       : exception;
   Illegal_Position     : exception;
   Illegal_Turnout_Info : exception;
   Missing_Turnout_Info : exception;

   -- OPERATIONS of Train_Position:  QUERIES ------------

   ------------------------------------------------------
   -- Check_Entering_Crossing
   -- sets Entering true if over a sensor that guards
   -- a crossing.  If so then Which will identify that Crossing.
   -- (Note that the tram crosses two such crossings.  When it
   -- enters North_Tram_Cross, it enters South_Tram_Cross also.)
   -- For avoiding collisions crossings are mutex regions.
   -- This procedure does not work for the two "crossovers"
   -- North_Crossover between sensors 34-39, 36-37; and
   -- South_Crossover between sensors 42-49, 44-47.
   -------------------------------------------------------
   procedure Check_Entering_Crossing (
         Pos      : in     Train_Position;
         Entering :    out Boolean;
         Which    :    out Crossing_Idx   );

   ------------------------------------------------------
   -- Check_Entering_Turnout
   -- sets Entering true if over a sensor that guards
   -- a turnout.  If so then Which will identify that turnout
   -- and Converging and Required_Setting will be changed to match.
   -- NOTE: not converging => Middle
   -- (v2.2) If Which denotes a chained turnout (feeds into, or
   -- may feed into depending on setting) another turnout
   -- (eg 9 into 10 or 18 in fwd polarity) then Chained will be
   -- set /= No.
   -------------------------------------------------------
   procedure Check_Entering_Turnout (
         Pos              : in     Train_Position;
         Entering         :    out Boolean;
         Which            :    out Turnout_Idx;
         Converging       :    out Boolean;
         Required_Setting :    out Turnout_Pos;
         Chained          :    out Chain_Type      );

   ------------------------------------------------------
   -- Check_Entering_Chained_Turnout     (v2.2)
   -- pre: 1. Check_Entering_Turnout has set Entering true
   --  and Chained /= No.
   --  2. Setting1 /= Middle and if converging Setting1 is
   --  consistent with Pos
   -- Sets Which2 to identify that the second turnout
   -- and Converging2 and Required_Setting2 will be changed to match.
   -- Setting1 must be set to the (assumed) position of the
   -- first turnout and must be correct whether the first turnout is
   -- diverging or converging, else Illegal_Turnout_Info is raised.
   -- Example: Pos is over sensor 47 in fwd polarity.  Entering = true,
   -- Which = 17, Chained = Immediate.  If Setting1 is Turned then
   -- Entering2 := true, Which2 := 10,  Converging2 := true,
   -- Required_Setting2 := Turned
   -- If pre-condition 1 is false then Entering2 := False,
   --    Which2 := No_Turnout.
   -- If precondition 2 is false then throws Illegal_Turnout_Info.
   -------------------------------------------------------
   procedure Check_Entering_Chained_Turnout (
         Pos               : in     Train_Position;
         Setting1          : in     Turnout_Pos;
         Entering2         :    out Boolean;
         Which2            :    out Turnout_Idx;
         Converging2       :    out Boolean;
         Required_Setting2 :    out Turnout_Pos     );

   ------------------------------------------------------
   -- Check_Entering_Zone
   -- sets Entering true if pos is over a sensor that guards
   -- a boundary zone.  If so then Which will identify that zone.
   -- (Zones are track sections either side of a boundary sensor
   -- For avoiding collisions they are mutex regions.)
   -------------------------------------------------------
   procedure Check_Entering_Zone (
         Pos      : in     Train_Position;
         Entering :    out Boolean;
         Which    :    out Zone_Id         );

   ------------------------------------------------------
   -- Check_Leaving_Crossing
   -- sets Leaving true if over a sensor that rear-guards
   -- a crossing.  If so then Which will identify that Crossing.
   -- (Note that the tram crosses two such crossings.  When it
   -- leaves one, it leaves both, nominally North_Tram_Crossing)
   -- For avoiding collisions crossings are mutex regions.
   -- This procedure does not work for the two "crossovers"
   -- North_Crossover between sensors 34-39, 36-37; and
   -- South_Crossover between sensors 42-49, 44-47.
   -------------------------------------------------------
   procedure Check_Leaving_Crossing (
         Pos     : in     Train_Position;
         Leaving :    out Boolean;
         Which   :    out Crossing_Idx    );

   -------------------------------------------------------
   -- Check_Leaving_Turnout
   -- sets Leaving true if over a sensor that rear-guards
   -- a turnout.  If so then Which will identify that turnout
   -- and Converging will match.  If this is the second of a pair
   -- of Immediate chained turnouts (no sensor between) then
   -- Chained:=Immediate else Chained:=No
   -- (note this is different to Check_Entering_Turnout)
   -------------------------------------------------------
   procedure Check_Leaving_Turnout (
         Pos        : in     Train_Position;
         Leaving    :    out Boolean;
         Which      :    out Turnout_Idx;
         Converging :    out Boolean;
         Chained    :    out Chain_Type   );  -- (v2.2)

   ------------------------------------------------------
   -- Check_Leaving_Chained_Turnout     (v2.2)
   -- pre: Check_Leaving_Turnout has set Chained = Immediate.
   -- We consider the current turnout to be number 2 and the one behind
   -- to be number 1.
   -- Setting2 is the position of the current.
   -- Which1 := identify that the turnout behind, No_Turnout if none.
   -- Converging1 is set to according to the turnout behind.
   -- If Setting2 is inconsistent with that needed by the second turnout
   -- and previos call of Check_Leaving Turnout set Converging=True
   -- then Illegal_Turnout_Info is raised.
   -- Blocks to release can be found using the array Turnout_Data and the known
   -- positions of both turnouts, together with the two Converging values.
   --
   -- Example: Pos is over sensor 47 in rev polarity.
   -- Check_Leaving_Turnout set Leaving = true,
   -- Which = 17, Converging = True, Chained = Immediate.
   -- If Setting1 is Straight then Leaving1 := true, Which1 := 18,
   --    Converging1 := false
   -- If Setting1 is Turned then Leaving1 := true, Which1 := 10,
   --    Converging1 := false
   -- If pre-condition is false, leaving1 := False,
   -- Which1 := No_Turnout, Converging1 := false.
   -------------------------------------------------------
   procedure Check_Leaving_Chained_Turnout (
         Pos         : in     Train_Position;
         Setting2    : in     Turnout_Pos;
         Leaving1    :    out Boolean;
         Which1      :    out Turnout_Idx;
         Converging1 :    out Boolean );

   -------------------------------------------------------
   -- Is_Leaving_Powered_Track
   -- returns true if Pos is over a sensor that guards
   -- dead track, eg Rev at sensor 3 in siding block 2.
   -------------------------------------------------------
   function Is_Leaving_Powered_Track (
         Pos     :  Train_Position  ) return Boolean;

   -------------------------------------------------------
   -- Check_Leaving_Zone
   -- sets Leaving true if over a sensor that rear-guards
   -- a zone.  If so then Which will identify that zone.
   -------------------------------------------------------
   procedure Check_Leaving_Zone (
         Pos     : in     Train_Position;
         Leaving :    out Boolean;
         Which   :    out Zone_Id         );

   ------------------------------------------------------
   -- Check_Turnout_Info_Need
   -- sets Needed true if a following call of Advance_Pos or
   -- Resume will need turnout position data.  If so then Which
   -- will identify that turnout.
   -- (obsolete for Advance_Pos - use 4-parameter Advance_Pos first
   -- and then the 3-parameter version if required)
   -- Use before Resume.
   -------------------------------------------------------
   procedure Check_Turnout_Info_Need (
         Pos         : in     Train_Position;
         Needed      :    out Boolean;
         For_Turnout :    out Turnout_Id      );

   ------------------------------------------------------
   -- Feature_At
   -- returns the feature at Pos
   -------------------------------------------------------
   function Feature_At (
         Pos : Train_Position )
     return Feature_Descriptor;

   ------------------------------------------------------
   -- Feature_Kind_At
   -- returns the kind of the feature at Pos (cheaper than Feature_At)
   -------------------------------------------------------
   function Feature_Kind_At (
         Pos : Train_Position )
     return Feature_Kind;

   ------------------------------------------------------
   -- Find_Next_Sensor_Along
   -- finds following sensor along the track moving in the
   -- direction Pos.Polr.  If Pos is actually on a sensor then
   -- this method finds the one after.
   -- Sets The_Sensor to No_Sensor if Is_Leaving_Powered_Track.
   -- If there is a need for turnout position data then
   -- The_Sensor:=No_Sensor, Need_Setting:=True, For_Turnout to
   -- the relevant one.
   -------------------------------------------------------
      procedure Find_Next_Sensor_Along(
      Pos          : in     Train_Position;
      The_Sensor   :    out Sensor_Idx;
      Need_Setting :    out Boolean;
      For_Turnout  :    out Turnout_Id );

   ------------------------------------------------------
   -- Find_Next_Sensor_Expected
   -- finds following sensor along the track moving in the
   -- direction Pos.Polr.
   -- Sets The_Sensor to No_Sensor if Is_Leaving_Powered_Track.
   -- If there is a need for turnout position data then
   -- The_Sensor:=No_Sensor, Need_Setting:=True, For_Turnout to
   -- the relevant one.  To recover from this, call Next_Sensor_Along
   -- using the relevant setting.
   -------------------------------------------------------
   procedure Find_Next_Sensor_Expected(
      Pos          : in     Train_Position;
      The_Sensor   :    out Sensor_Idx;
      Need_Setting :    out Boolean;
      For_Turnout  :    out Turnout_Id );

   ------------------------------------------------------
   -- Is_Sensor_At
   -- returns true if Pos is over The_Sensor.
   -- ( Is_Sensor_At and Is_Sensor_Ahead_Of are disjoint.)
   -------------------------------------------------------
   function Is_Sensor_At (
         Pos        : Train_Position;
         The_Sensor : Sensor_Id       )
     return Boolean;

   ------------------------------------------------------
   -- Is_Sensor_Ahead_Of
   -- returns true if The_Sensor is next ahead of the current Pos.
   -- Calls Next_Sensor(pos)
   -- obsolete? (not implemented)
   -------------------------------------------------------
   function Is_Sensor_Ahead_Of (
         Pos        : Train_Position;
         The_Sensor : Sensor_Id       )
     return Boolean;

   ------------------------------------------------------
   -- Next_Sensor_Along (pos)  (removed, obsolete. v2.5)
   -------------------------------------------------------
   --function Next_Sensor_Along (
   --      Pos : Train_Position )
   --  return Sensor_Idx;

   ------------------------------------------------------
   -- Next_Sensor_Along (pos, turnpos)
   -- returns following sensor id along a block
   -- moving in the direction Pos.Polr
   -- If Pos is actually on a sensor then this method returns
   -- the id of the one after.
   -- Assumes the supplied turnout setting data is for the
   -- relevant turnout eg as reported by Find_Next_Sensor_Along.
   -- Ignores it if not needed.
   -- Raises Missing_Turnout_Info if Middle supplied when
   -- Straight or Turned is needed.
   -------------------------------------------------------
   function Next_Sensor_Along (
         Pos     : Train_Position;
         Setting : Turnout_Pos     )
     return Sensor_Idx;

   ------------------------------------------------------
   -- Next_Sensor_Expected (pos)   (removed, obsolete. v2.5)
   -- returns id of sensor which will next produce an event (could
   -- be the current one if pos is over a sensor) moving in the
   -- direction Pos.Polr
   -------------------------------------------------------
   --function Next_Sensor_Expected (
   --      Pos : Train_Position )
   --  return Sensor_Idx;

   ------------------------------------------------------
   -- Next_Sensor_Expected (pos, turnsetting)
   -- returns id of sensor which will next produce an event (could
   -- be the current one if pos is over a sensor) moving in the
   -- direction Pos.Polr
   -- Assumes the supplied turnout setting data is for the
   -- relevant turnout eg as reported by Find_Next_Sensor_Expected.
   -- Ignores it if not needed.
   -- Raises Missing_Turnout_Info if Middle supplied when
   -- Straight or Turned is needed.
   -- May need to be called after Turn_Around.
   -------------------------------------------------------
   function Next_Sensor_Expected (
         Pos     : Train_Position;
         Setting : Turnout_Pos     )
     return Sensor_Idx;


   -- operations of Train_Position:  MUTATORS ------------

   -------------------------------------------------------
   -- Initialise_After (sensor)
   -- (v2.4)
   -- Returns a Pos that is after the sensor in the normal direction
   -- i.e. in Fwd polarity
   -- Raises Illegal_Position if the implied position is on a
   -- turnout or if the sensor is not installed.
   -------------------------------------------------------
   function Initialise_After (The_Sensor : Sensor_Id)
      return Train_Position;

   -------------------------------------------------------
   -- Advance_Pos (pos,stopping, need_setting, for_turnout)
   -- (v2.3)
   -- Assuming not Stopping, changes Pos to the following value
   -- moving along a block in the direction Pos.Polr.
   -- If need turnout position data for a diverging turnout
   -- then Need_Setting:=True and For_Turnout:=<which one>
   -- and Pos will not be changed.
   -- If advancing into an immediate chained pair of turnouts then
   -- Pos will jump (immediately) to the second turnout.
   --
   -- Pos.Block_Num will be changed: (1) When entering
   -- a turnout this will be set to the centre block.
   -- (2) In the case of a boundary sensor Block_Num will not change
   -- until Pos advances off the sensor.  The value while over the
   -- sensor will be notionally where Pos came from.
   --
   -- Pos.Polr will be changed if going over an inverting join into
   -- or out of a turnout.  When on a turnout Polr will be appropriate
   -- for the central part of the turnout (consistent with Block_Num).
   --
   -- Stopping means the train has zero power and is skidding
   -- to a stop and this Pos is at the leading end of the train.
   -- (Stopping should always be false for the rear.)
   -- If Stopping and Feature_Kind_At(Pos)=Sensor then this
   -- operation will change Pos.Substate to Just_After and make
   -- no other changes - these are deferred until procedure
   -- Resume is called later.
   --
   -- Normally this procedure defines result.Next_Expected but
   -- this will be value No_Sensor if the new position
   -- 1) in on unpowered track (eg end of sidings), or
   -- 2) is on the first turnout of an immediate chained pair
   -- and it is converging and the second is diverging. (This
   -- occurs once in the 2008 layout, namely block 11).  In this
   -- case call Next_Sensor_Along with the setting of the
   -- second (diverging) turnout.
   -------------------------------------------------------
   procedure Advance_Pos (
         Pos          : in out Train_Position;
         Stopping     : in     Boolean;
         Need_Setting :    out Boolean;
         For_Turnout  :    out Turnout_Id
       );

   -------------------------------------------------------
   -- Advance_Pos (pos,stopping, setting)
   -- (v2.3)
   -- Assuming not Stopping, changes Pos to the following value
   -- moving along a block in the direction Pos.Polr.
   -- Setting is assumed to be the turnout position data for
   -- the diverging turnout.  (When there are immediate chained
   -- turnouts the first could be converging or diverging.)
   -- If Middle is supplied and a value /= Middle is needed to
   -- "advance" then exception Missing_Turnout_Info will be raised
   -- and Pos will not be changed.
   -- If advancing into an immediate chained pair of turnouts then
   -- Pos will be on the first turnout.
   --
   -- Pos.Block_Num will be changed: (1) When entering
   -- a turnout this will be set to the centre block.
   -- (2) In the case of a boundary sensor Block_Num will not change
   -- until Pos advances off the sensor.  The value while over the
   -- sensor will be notionally where Pos came from.
   --
   -- Pos.Polr will be changed if going over an inverting join into
   -- or out of a turnout.  When on a Turnout Polr will be appropriate
   -- for the central part of the turnout (consistent with Block_Num).
   --
   -- Stopping means the train has zero power and is skidding
   -- to a stop and this Pos is at the leading end of the train.
   -- (Stopping should always be false for the rear.)
   -- If Stopping and Feature_Kind_At(Pos)=Sensor then this
   -- operation will change Pos.Substate to Just_After and make
   -- no other changes - these are deferred until procedure
   -- Resume is called later.
   --
   -- Normally this procedure defines result.Next_Expected but
   -- this will be value No_Sensor if the new position
   -- 1) in on unpowered track (eg end of sidings), or
   -- 2) is on the first turnout of an immediate chained pair
   -- and it is converging and the second is diverging and
   -- Setting=Middle.  In this case repair Pos.Next_Expected by
   -- calling Next_Sensor_Along with the setting of the
   -- second (diverging) turnout.
   -------------------------------------------------------
   procedure Advance_Pos (
         Pos      : in out Train_Position;
         Stopping : in     Boolean;
         Setting  : in     Turnout_Pos
      );

   -------------------------------------------------------
   -- Next_Pos (pos [,stopping])  obsolete: use Advance_Pos
   -------------------------------------------------------
   --function Next_Pos (
   --      Pos      : Train_Position;
   --      Stopping : Boolean        := False )
   --  return Train_Position;

   ------------------------------------------------------
   -- Next_Pos (pos, turnoutsetting [,stopping])  obsolete
   -------------------------------------------------------
   --function Next_Pos (
   --      Pos      : Train_Position;
   --      Setting  : Turnout_Pos;
   --      Stopping : Boolean        := False )
   --  return Train_Position;

   ------------------------------------------------------
   -- Resume (pos, [turnoutsetting])
   -- Adjusts pos.substate and all other attributes to truly
   -- represent the position including Next_Expected.  Call
   -- this when motion is resumed after a train has stopped
   -- over a sensor but skids past (so that pos.Substate is
   -- Just_After or if Turn_Around has been called after that,
   -- Just_Before).
   -- Ensures pos.Substate=Norm and executes deferred advance code.
   -- Call for both ends of a train.
   -- Call Check_Turnout_Info_Need before each to ensure the
   -- appropriate Setting is provided.
   -- Raises Illegal_Position if substate /= Norm
   -- and Feature_Kind_At(Pos) /= Sensor -- clearly error.
   -- Raises Missing_Turnout_Info if a value for Setting is omitted
   -- or Middle is supplied but it is needed (as in Advance_Pos).
   -------------------------------------------------------
   procedure Resume (
         Pos     : in out Train_Position;
         Setting : in     Turnout_Pos := Middle     );

   ------------------------------------------------------
   -- Turn_Around (pos)
   -- Adjusts polarity, substate, block_num and next_Expected
   -- attributes of pos.
   -- Assumes client code has swapped the front and
   -- rear position variables of a train and then calls this
   -- operation on each.
   -- If Pos is over an immediate chained pair of turnouts then it
   -- may be over either of the pair; the next Advance_Pos will cope.
   -- If over a boundary sensor then Block_Num could be either.
   -- Sets Next_Expected to No_Sensor if turnout information is
   -- needed for this or if the new direction is into unpowered
   -- track (eg end of a siding).
   -- Call Find_Next_Sensor_Expected for each end after Turn_Around.
   -------------------------------------------------------
   procedure Turn_Around (
         Pos : in out Train_Position );

end Topolog2;